今天要來討論比較不一樣的主題,因為還是以 React 生態系為主,經常會用到元件化的開發方式,常常把關注點放在 HTML 與 JS 如何搭配,卻比較少討論到 CSS。
因此,今天讓我們來討論,元件化的 CSS 如何處理吧!
在還沒有使用 React 這樣的前端框架之前,我們會提倡 HTML、CSS、JS 三種檔案分離,透過檔案類型來做關注點分離(SoC),起碼確保不會所有樣式跟程式邏輯都擠在 index.html 裡面。
不過這樣的作法到了 React 這邊來,就顯得有點綁手綁腳,因為 React 使用 JSX,概念上就是把 HTML 寫在 .js
檔案裡面,這是因為 React 的關注點分離,是以「元件」為單位,把一個元件的 HTML、JavaScript 都寫在一個檔案裡面,這樣在做元件化開發的時候會比較方便。
那既然 HTML、JavaScript 都已經放在一起了,CSS 人呢?
如果還是按照傳統的做法,CSS 拉出來獨立的 .css
檔案,然後再透過 index.html
去安插 <link rel="stylesheet" href="main.css" />
的方式引入進來,這樣很容易會讓各元件的 css 混雜,而且 scope 範圍容易太大,導致不小心有同樣名字的 class 互相覆蓋。
在這之前,我們要先來了解 CSS-in-JS 是怎麼一回事。
如同上面提到的,為了要以「元件」為開發單位,需要限制 CSS 的作用範圍,以元件為 scope,換句話說就是,要讓每個元件的 CSS 都是獨立的,這樣就可以避免元件之間的 CSS 互相影響覆蓋。
另外,可以讓元件容易維護,也更容易重複使用,因為我可以很清楚知道:
其中,CSS-in-JS 的應用有像是 css modules、emotion、styled-components,今天就以 styled-components 來討論。
styled-components 提供了在 JavaScript 中直接撰寫 CSS 的介面,因為本體是 JavaScript,所以你可以做到:
比如像官網的例子:
import styled, { css } from 'styled-components';
const Button = styled.button`
background: transparent;
border-radius: 3px;
border: 2px solid palevioletred;
color: palevioletred;
margin: 0.5em 1em;
padding: 0.25em 1em;
${props => props.primary && css`
background: palevioletred;
color: white;
`}
`;
const Container = styled.div`
text-align: center;
`
render(
<Container>
<Button>Normal Button</Button>
<Button primary>Primary Button</Button>
</Container>
);
上面的 code 跑出來會像這樣:
可以看到 styled-components 的寫法,是使用反引號框起來的,也就是 ES6 的樣板語言語法。框起來的部分雖然看起來像是字串,但實際上會被轉換為可運行的 CSS style,當然也包括了用 ${}
框起來的 JS expression,而這也是 CSS-in-JS 強大的地方,能夠寫一些簡單的判斷邏輯,「在 CSS 裡面寫 JS」。
整體來說,CSS-in-JS 的這一類工具的用途算是滿明確的,因為只要是使用元件化開發,關注點分離的方式以「元件」為單位,就需要將 HTML、CSS 寫在 JavaScript 內,更能夠提高可維護性。
身為前端工程師,要關心的事情太多,要學的技術太多,有時候 CSS 會覺得「能跑就好」,「看起來差不多就好」,但事實上是,CSS 背後的世界相當廣大,隨著元件化開發的興起,CSS 也要能跟上這個趨勢。
其實我算是先跟著專案開始寫 styled-component,才開始理解 CSS-in-JS 背後的概念原因,如果沒有特別去理解的話,其實還真的不知道為什麼要用這個工具呢!
styled-components
把 CSS 寫在 JavaScript 中!? - CSS in JS 的使用
你好~
針對文章中提到的以下兩點想要請教:
- styled component 的優點之一是就近看到每個元件的 CSS,不需要頻繁切換檔案
- 語法的 highlight、自動完成、防呆較不完整,需另外裝 plugin 處理
再麻煩你有空幫我解惑~謝謝你!
你好~
關於第一點,其實我不確定有沒有 best practice 可以效法,所以我提一下自己的考量,如果其他邦友可以幫協助補充就更好了~
我們公司的專案大多會採用你提到的做法二,也就是在 Button.component.jsx 裡頭直接寫 styled-component
原因其實滿單純是因為,我們每個元件都會傾向拆小一點,盡量可以共用,因此一些小的基底元件,可能也就一兩個 styled-component 組成,而大的元件也都是組合那些小元件來的,不太會有 styled-component,就懶得再拆額外一隻檔案來處理了
但我想,如果元件的 UI 邏輯比較複雜,拆出去也會讓程式看起來不會那麼雜,我想這部分的取捨,最好也經過團隊一致共識,比較不會亂。
關於第二點,可以參考這個 VS code plugin
感謝你的回覆~